<!DOCTYPE html>
<!--
Copyright (c) 2012 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/extras/importer/linux_perf/parser.html">

<script>
'use strict';

/**
 * @fileoverview Parses i915 driver events in the Linux event trace format.
 */
tr.exportTo('tr.e.importer.linux_perf', function() {
  const ColorScheme = tr.b.ColorScheme;
  const Parser = tr.e.importer.linux_perf.Parser;

  /**
   * Parses linux i915 trace events.
   * @constructor
   */
  function I915Parser(importer) {
    Parser.call(this, importer);

    importer.registerEventHandler('i915_gem_object_create',
        I915Parser.prototype.gemObjectCreateEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_bind',
        I915Parser.prototype.gemObjectBindEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_unbind',
        I915Parser.prototype.gemObjectBindEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_change_domain',
        I915Parser.prototype.gemObjectChangeDomainEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_pread',
        I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_pwrite',
        I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_fault',
        I915Parser.prototype.gemObjectFaultEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_clflush',
        // NB: reuse destroy handler
        I915Parser.prototype.gemObjectDestroyEvent.bind(this));
    importer.registerEventHandler('i915_gem_object_destroy',
        I915Parser.prototype.gemObjectDestroyEvent.bind(this));
    importer.registerEventHandler('i915_gem_ring_dispatch',
        I915Parser.prototype.gemRingDispatchEvent.bind(this));
    importer.registerEventHandler('i915_gem_ring_flush',
        I915Parser.prototype.gemRingFlushEvent.bind(this));
    importer.registerEventHandler('i915_gem_request',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_request_add',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_request_complete',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_request_retire',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_request_wait_begin',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_request_wait_end',
        I915Parser.prototype.gemRequestEvent.bind(this));
    importer.registerEventHandler('i915_gem_ring_wait_begin',
        I915Parser.prototype.gemRingWaitEvent.bind(this));
    importer.registerEventHandler('i915_gem_ring_wait_end',
        I915Parser.prototype.gemRingWaitEvent.bind(this));
    importer.registerEventHandler('i915_reg_rw',
        I915Parser.prototype.regRWEvent.bind(this));
    importer.registerEventHandler('i915_flip_request',
        I915Parser.prototype.flipEvent.bind(this));
    importer.registerEventHandler('i915_flip_complete',
        I915Parser.prototype.flipEvent.bind(this));
    importer.registerEventHandler('intel_gpu_freq_change',
        I915Parser.prototype.gpuFrequency.bind(this));
  }

  I915Parser.prototype = {
    __proto__: Parser.prototype,

    i915FlipOpenSlice(ts, obj, plane) {
      // use i915_flip_obj_plane?
      const kthread = this.importer.getOrCreatePseudoThread('i915_flip');
      kthread.openSliceTS = ts;
      kthread.openSlice = 'flip:' + obj + '/' + plane;
    },

    i915FlipCloseSlice(ts, args) {
      const kthread = this.importer.getOrCreatePseudoThread('i915_flip');
      if (kthread.openSlice) {
        const slice = new tr.model.ThreadSlice('', kthread.openSlice,
            ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),
            kthread.openSliceTS,
            args,
            ts - kthread.openSliceTS);

        kthread.thread.sliceGroup.pushSlice(slice);
      }
      kthread.openSlice = undefined;
    },

    i915GemObjectSlice(ts, eventName, obj, args) {
      const kthread = this.importer.getOrCreatePseudoThread('i915_gem');
      kthread.openSlice = eventName + ':' + obj;
      const slice = new tr.model.ThreadSlice('', kthread.openSlice,
          ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),
          ts, args, 0);

      kthread.thread.sliceGroup.pushSlice(slice);
    },

    i915GemRingSlice(ts, eventName, dev, ring, args) {
      const kthread = this.importer.getOrCreatePseudoThread('i915_gem_ring');
      kthread.openSlice = eventName + ':' + dev + '.' + ring;
      const slice = new tr.model.ThreadSlice('', kthread.openSlice,
          ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),
          ts, args, 0);

      kthread.thread.sliceGroup.pushSlice(slice);
    },

    i915RegSlice(ts, eventName, reg, args) {
      const kthread = this.importer.getOrCreatePseudoThread('i915_reg');
      kthread.openSlice = eventName + ':' + reg;
      const slice = new tr.model.ThreadSlice('', kthread.openSlice,
          ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),
          ts, args, 0);

      kthread.thread.sliceGroup.pushSlice(slice);
    },

    i915FreqChangeSlice(ts, eventName, args) {
      const kthread = this.importer.getOrCreatePseudoThread('i915_gpu_freq');
      kthread.openSlice = eventName;
      const slice = new tr.model.ThreadSlice('', kthread.openSlice,
          ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),
          ts, args, 0);

      kthread.thread.sliceGroup.pushSlice(slice);
    },

    /**
     * Parses i915 driver events and sets up state in the importer.
     */
    gemObjectCreateEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /obj=(\w+), size=(\d+)/.exec(eventBase.details);
      if (!event) return false;

      const obj = event[1];
      const size = parseInt(event[2]);
      this.i915GemObjectSlice(ts, eventName, obj,
          {
            obj,
            size
          });
      return true;
    },

    gemObjectBindEvent(eventName, cpuNumber, pid, ts, eventBase) {
      // TODO(sleffler) mappable
      const event = /obj=(\w+), offset=(\w+), size=(\d+)/.exec(
          eventBase.details);
      if (!event) return false;

      const obj = event[1];
      const offset = event[2];
      const size = parseInt(event[3]);
      this.i915ObjectGemSlice(ts, eventName + ':' + obj,
          {
            obj,
            offset,
            size
          });
      return true;
    },

    gemObjectChangeDomainEvent(eventName, cpuNumber, pid, ts,
        eventBase) {
      const event = /obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/.exec(
          eventBase.details);
      if (!event) return false;

      const obj = event[1];
      const read = event[2];
      const write = event[3];
      this.i915GemObjectSlice(ts, eventName, obj,
          {
            obj,
            read,
            write
          });
      return true;
    },

    gemObjectPreadWriteEvent(eventName, cpuNumber, pid, ts,
        eventBase) {
      const event = /obj=(\w+), offset=(\d+), len=(\d+)/.exec(
          eventBase.details);
      if (!event) return false;

      const obj = event[1];
      const offset = parseInt(event[2]);
      const len = parseInt(event[3]);
      this.i915GemObjectSlice(ts, eventName, obj,
          {
            obj,
            offset,
            len
          });
      return true;
    },

    gemObjectFaultEvent(eventName, cpuNumber, pid, ts, eventBase) {
      // TODO(sleffler) writable
      const event = /obj=(\w+), (\w+) index=(\d+)/.exec(eventBase.details);
      if (!event) return false;

      const obj = event[1];
      const type = event[2];
      const index = parseInt(event[3]);
      this.i915GemObjectSlice(ts, eventName, obj,
          {
            obj,
            type,
            index
          });
      return true;
    },

    gemObjectDestroyEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /obj=(\w+)/.exec(eventBase.details);
      if (!event) return false;

      const obj = event[1];
      this.i915GemObjectSlice(ts, eventName, obj,
          {
            obj
          });
      return true;
    },

    gemRingDispatchEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(
          eventBase.details);
      if (!event) return false;

      const dev = parseInt(event[1]);
      const ring = parseInt(event[2]);
      const seqno = parseInt(event[3]);
      this.i915GemRingSlice(ts, eventName, dev, ring,
          {
            dev,
            ring,
            seqno
          });
      return true;
    },

    gemRingFlushEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/
          .exec(eventBase.details);
      if (!event) return false;

      const dev = parseInt(event[1]);
      const ring = parseInt(event[2]);
      const invalidate = event[3];
      const flush = event[4];
      this.i915GemRingSlice(ts, eventName, dev, ring,
          {
            dev,
            ring,
            invalidate,
            flush
          });
      return true;
    },

    gemRequestEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(
          eventBase.details);
      if (!event) return false;

      const dev = parseInt(event[1]);
      const ring = parseInt(event[2]);
      const seqno = parseInt(event[3]);
      this.i915GemRingSlice(ts, eventName, dev, ring,
          {
            dev,
            ring,
            seqno
          });
      return true;
    },

    gemRingWaitEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /dev=(\d+), ring=(\d+)/.exec(eventBase.details);
      if (!event) return false;

      const dev = parseInt(event[1]);
      const ring = parseInt(event[2]);
      this.i915GemRingSlice(ts, eventName, dev, ring,
          {
            dev,
            ring
          });
      return true;
    },

    regRWEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/
          .exec(eventBase.details);
      if (!event) return false;

      const rw = event[1];
      const reg = event[2];
      const len = event[3];
      const data = event[3];
      this.i915RegSlice(ts, rw, reg,
          {
            rw,
            reg,
            len,
            data
          });
      return true;
    },

    flipEvent(eventName, cpuNumber, pid, ts, eventBase) {
      const event = /plane=(\d+), obj=(\w+)/.exec(eventBase.details);
      if (!event) return false;

      const plane = parseInt(event[1]);
      const obj = event[2];
      if (eventName === 'i915_flip_request') {
        this.i915FlipOpenSlice(ts, obj, plane);
      } else {
        this.i915FlipCloseSlice(ts,
            {
              obj,
              plane
            });
      }
      return true;
    },

    gpuFrequency(eventName, cpuNumver, pid, ts, eventBase) {
      const event = /new_freq=(\d+)/.exec(eventBase.details);
      if (!event) return false;
      const freq = parseInt(event[1]);

      this.i915FreqChangeSlice(ts, eventName, {
        freq
      });
      return true;
    }
  };

  Parser.register(I915Parser);

  return {
    I915Parser,
  };
});
</script>

